all repos — caroster @ 3e13ed81f9961ed582333d0e9296921db2ed91d9

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Tooltip from '@mui/material/Tooltip';
  3import IconButton from '@mui/material/IconButton';
  4import Box from '@mui/material/Box';
  5import Link from '@mui/material/Link';
  6import Card from '@mui/material/Card';
  7import Container from '@mui/material/Container';
  8import TextField from '@mui/material/TextField';
  9import Typography from '@mui/material/Typography';
 10import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';
 11import EventIcon from '@mui/icons-material/Event';
 12import TuneIcon from '@mui/icons-material/Tune';
 13import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 14import {useTheme} from '@mui/material/styles';
 15import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 16import {PropsWithChildren, useState} from 'react';
 17import {useTranslation} from 'react-i18next';
 18import pageUtils from '../../../lib/pageUtils';
 19import ShareEvent from '../../../containers/ShareEvent';
 20import useEventStore from '../../../stores/useEventStore';
 21import useToastStore from '../../../stores/useToastStore';
 22import EventLayout, {TabComponent} from '../../../layouts/Event';
 23import {
 24  EventByUuidDocument,
 25  useUpdateEventMutation,
 26} from '../../../generated/graphql';
 27import AddressAutofill from '../../../containers/AddressAutofill';
 28
 29interface Props {
 30  eventUUID: string;
 31  announcement?: string;
 32}
 33
 34const Page = (props: PropsWithChildren<Props>) => {
 35  return <EventLayout {...props} Tab={DetailsTab} />;
 36};
 37
 38const DetailsTab: TabComponent = ({}) => {
 39  const {t} = useTranslation();
 40  const theme = useTheme();
 41  const [updateEvent] = useUpdateEventMutation();
 42  const addToast = useToastStore(s => s.addToast);
 43  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 44  const event = useEventStore(s => s.event);
 45  const [isEditing, setIsEditing] = useState(false);
 46
 47  const onSave = async e => {
 48    try {
 49      const {uuid, ...data} = event;
 50      const {id, travels, waitingPassengers, __typename, ...input} = data;
 51      await updateEvent({
 52        variables: {
 53          uuid,
 54          eventUpdate: {
 55            ...input,
 56          },
 57        },
 58        refetchQueries: ['eventByUUID'],
 59      });
 60      setIsEditing(false);
 61    } catch (error) {
 62      console.error(error);
 63      addToast(t('event.errors.cant_update'));
 64    }
 65  };
 66
 67  const modifyButton = isEditing ? (
 68    <Tooltip
 69      title={t('event.details.save')}
 70      sx={{
 71        position: 'absolute',
 72        top: theme.spacing(2),
 73        right: theme.spacing(2),
 74      }}
 75    >
 76      <IconButton color="primary" onClick={onSave}>
 77        <CheckCircleOutlineIcon />
 78      </IconButton>
 79    </Tooltip>
 80  ) : (
 81    <Tooltip
 82      title={t('event.details.modify')}
 83      sx={{
 84        position: 'absolute',
 85        top: theme.spacing(2),
 86        right: theme.spacing(2),
 87      }}
 88    >
 89      <IconButton color="primary" onClick={() => setIsEditing(true)}>
 90        <TuneIcon />
 91      </IconButton>
 92    </Tooltip>
 93  );
 94
 95  if (!event) return null;
 96
 97  return (
 98    <Box
 99      sx={{
100        position: 'relative',
101      }}
102    >
103      <Container
104        sx={{
105          p: 4,
106          mt: 6,
107          mb: 11,
108          mx: 0,
109          [theme.breakpoints.down('md')]: {
110            p: 2,
111          },
112        }}
113      >
114        <Card
115          sx={{
116            position: 'relative',
117            maxWidth: '100%',
118            width: '350px',
119            p: 2,
120          }}
121        >
122          <Typography variant="h4" pb={2}>
123            {t('event.details')}
124          </Typography>
125          {modifyButton}
126          <Box pt={2} pr={1.5}>
127            <Typography variant="overline">{t('event.fields.name')}</Typography>
128            <Typography variant="body1">
129              {isEditing ? (
130                <TextField
131                  size="small"
132                  fullWidth
133                  value={event.name}
134                  onChange={e => setEventUpdate({name: e.target.value})}
135                  name="name"
136                  id="EditEventName"
137                />
138              ) : (
139                <Typography variant="body1" id="EventName">
140                  {event.name ?? t('event.fields.empty')}
141                </Typography>
142              )}
143            </Typography>
144          </Box>
145          <Box pt={2} pr={1.5}>
146            <Typography variant="overline">{t('event.fields.date')}</Typography>
147            {isEditing ? (
148              <Typography variant="body1">
149                <DatePicker
150                  slotProps={{
151                    textField: {
152                      size: 'small',
153                      id: `EditEventDate`,
154                      fullWidth: true,
155                      placeholder: t('event.fields.date_placeholder'),
156                    },
157                  }}
158                  format="DD/MM/YYYY"
159                  value={moment(event.date)}
160                  onChange={date =>
161                    setEventUpdate({
162                      date: !date ? null : moment(date).format('YYYY-MM-DD'),
163                    })
164                  }
165                />
166              </Typography>
167            ) : (
168              <Box position="relative">
169                <Typography variant="body1" id="EventDate">
170                  {event.date
171                    ? moment(event.date).format('DD/MM/YYYY')
172                    : t('event.fields.empty')}
173                </Typography>
174                <EventIcon
175                  color="action"
176                  sx={{
177                    position: 'absolute',
178                    right: theme.spacing(-0.5),
179                    top: 0,
180                  }}
181                />
182              </Box>
183            )}
184          </Box>
185          <Box pt={2} pr={1.5}>
186            <Typography variant="overline">
187              {t('event.fields.address')}
188            </Typography>
189            {isEditing ? (
190              <AddressAutofill
191                label={t('event.creation.address')}
192                address={event.address}
193                onSelect={({location, address}) => {
194                  setEventUpdate({
195                    address,
196                    latitude: location[1],
197                    longitude: location[0],
198                  });
199                }}
200              />
201            ) : (
202              <Box position="relative">
203                <Typography variant="body1" id="EventAddress" sx={{pr: 3}}>
204                  {event.address ? (
205                    <Link
206                      target="_blank"
207                      rel="noreferrer"
208                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
209                        event.address
210                      )}`}
211                      onClick={e => e.preventDefault}
212                    >
213                      {event.address}
214                    </Link>
215                  ) : (
216                    t('event.fields.empty')
217                  )}
218                </Typography>
219                <PlaceOutlinedIcon
220                  color="action"
221                  sx={{
222                    position: 'absolute',
223                    right: theme.spacing(-0.5),
224                    top: 0,
225                  }}
226                />
227              </Box>
228            )}
229          </Box>
230          <Box pt={2} pr={1.5}>
231            <Typography variant="overline">
232              {t('event.fields.description')}
233            </Typography>
234            {isEditing ? (
235              <Typography variant="body1">
236                <TextField
237                  fullWidth
238                  multiline
239                  maxRows={4}
240                  inputProps={{maxLength: 250}}
241                  value={event.description || ''}
242                  onChange={e => setEventUpdate({description: e.target.value})}
243                  id={`EditEventDescription`}
244                  name="description"
245                />
246              </Typography>
247            ) : (
248              <Typography variant="body1" id="EventDescription" sx={{pr: 3}}>
249                {event.description ?? t('event.fields.empty')}
250              </Typography>
251            )}
252          </Box>
253          {!isEditing && (
254            <ShareEvent
255              title={`Caroster ${event.name}`}
256              sx={{width: '100%', mt: 2}}
257            />
258          )}
259        </Card>
260      </Container>
261    </Box>
262  );
263};
264
265export const getServerSideProps = pageUtils.getServerSideProps(
266  async (context, apolloClient) => {
267    const {uuid} = context.query;
268    const {host = ''} = context.req.headers;
269    let event = null;
270
271    // Fetch event
272    try {
273      const {data} = await apolloClient.query({
274        query: EventByUuidDocument,
275        variables: {uuid},
276      });
277      event = data?.eventByUUID?.data;
278    } catch (error) {
279      return {
280        notFound: true,
281      };
282    }
283
284    return {
285      props: {
286        eventUUID: uuid,
287        metas: {
288          title: event?.attributes?.name || '',
289          url: `https://${host}${context.resolvedUrl}`,
290        },
291      },
292    };
293  }
294);
295export default Page;